package com.ms.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ms.constant.MessageTypeConstant;
import com.ms.domain.entity.Follow;
import com.ms.domain.entity.UserStats;
import com.ms.exception.BusinessException;
import com.ms.mapper.FollowMapper;
import com.ms.mapper.UserStatsMapper;
import com.ms.service.FollowService;
import com.ms.service.MessageService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class FollowServiceImpl extends ServiceImpl<FollowMapper, Follow> implements FollowService {

    private final FollowMapper followMapper;
    private final UserStatsMapper userStatsMapper;
    private final MessageService messageService;

    /**
     * 关注用户
     * @param followerId 关注者ID
     * @param followingId 被关注者ID
     */
    @Override
    @Transactional
    @Caching(evict = {
            // 关注者自己的关注列表
            @CacheEvict(value = "following", key = "#followerId", allEntries = true),
            // 被关注者的粉丝列表
            @CacheEvict(value = "followers", key = "#followingId", allEntries = true),
            // 双方统计数据
            @CacheEvict(value = "userStats", key = "#followerId"),
            @CacheEvict(value = "userStats", key = "#followingId"),
            // 单条关系缓存
            @CacheEvict(value = "following",
                        key = "T(com.ms.constant.CacheConstant).IS_FOLLOWING + ':' + #followerId + ':' + #followingId")
    })
    public void followUser(Integer followerId, Integer followingId) {
        if (followerId.equals(followingId)) {
            throw new BusinessException("不能关注自己");
        }

        if (isFollowing(followerId, followingId)) {
            throw new BusinessException("已经关注该用户");
        }

        // 1. 写入关注记录
        Follow follow = Follow.builder()
                .followerId(followerId)
                .followingId(followingId)
                .build();
        followMapper.insert(follow);

        // 2. 原子更新关注者 & 被关注者的统计
        userStatsMapper.update(null, new UpdateWrapper<UserStats>()
                .eq("user_id", followerId)
                .setSql("following_count = following_count + 1")
                .setSql("update_time = NOW()"));

        userStatsMapper.update(null, new UpdateWrapper<UserStats>()
                .eq("user_id", followingId)
                .setSql("followers_count = followers_count + 1")
                .setSql("update_time = NOW()"));

        // 3. 发送关注通知
        messageService.sendMessage(followingId,
                "关注通知",
                MessageTypeConstant.FOLLOW,
                "您有一个新的关注者",
                followerId);

        log.info("用户 {} 关注了用户 {}", followerId, followingId);
    }


    /**
     * 取消关注用户
     * @param followerId 关注者ID
     * @param followingId 被关注者ID
     */
    @Override
    @Transactional
    @Caching(evict = {
            @CacheEvict(value = "following", key = "#followerId", allEntries = true),
            @CacheEvict(value = "followers", key = "#followingId", allEntries = true),
            @CacheEvict(value = "userStats", key = "#followerId"),
            @CacheEvict(value = "userStats", key = "#followingId"),
            // 与 followUser 保持一致，使用 CacheConstant
            @CacheEvict(value = "following",
                    key = "T(com.ms.constant.CacheConstant).IS_FOLLOWING + ':' + #followerId + ':' + #followingId")
    })
    public void unfollowUser(Integer followerId, Integer followingId) {
        if (!isFollowing(followerId, followingId)) {
            throw new BusinessException("尚未关注该用户");
        }

        // 1. 删除关注记录
        followMapper.delete(new QueryWrapper<Follow>()
                .eq("follower_id", followerId)
                .eq("following_id", followingId));

        // 2. 原子自减双方计数
        userStatsMapper.update(null, new UpdateWrapper<UserStats>()
                .eq("user_id", followerId)
                .setSql("following_count = following_count - 1")
                .setSql("update_time = NOW()"));

        userStatsMapper.update(null, new UpdateWrapper<UserStats>()
                .eq("user_id", followingId)
                .setSql("followers_count = followers_count - 1")
                .setSql("update_time = NOW()"));

        log.info("用户 {} 取消关注了用户 {}", followerId, followingId);
    }


    /**
     * 判断是否已经关注
     * @param followerId 关注者ID
     * @param followingId 被关注者ID
     */
    @Override
    @Cacheable(value = "following",
            key = "T(com.ms.constant.CacheConstant).IS_FOLLOWING + ':' + #followerId + ':' + #followingId",
            unless = "#result == false")
    public boolean isFollowing(Integer followerId, Integer followingId) {
        return followMapper.selectCount(new QueryWrapper<Follow>()
                .eq("follower_id", followerId)
                .eq("following_id", followingId)) > 0;
    }

    /**
     * 分页获取用户的关注列表
     * @param userId 用户ID
     * @param page 页码
     * @param size 每页数量
     * @return 关注列表
     */
    @Override
    @Cacheable(value = "following",
            key = "T(com.ms.constant.CacheConstant).FOLLOWING_LIST + ':' + #userId + ':' + #page + ':' + #size",
            unless = "#result == null || #result.records.isEmpty()")
    public Page<Follow> getFollowingList(Integer userId, Integer page, Integer size) {
        Page<Follow> followPage = new Page<>(page, size);
        return followMapper.selectPage(followPage, new QueryWrapper<Follow>()
                .eq("follower_id", userId)
                .orderByDesc("create_time"));
    }

    /**
     * 分页获取用户的粉丝列表
     * @param userId 用户ID
     * @param page 页码
     * @param size 每页数量
     */
    @Override
    @Cacheable(value = "followers",
            key = "T(com.ms.constant.CacheConstant).FOLLOWERS_LIST + ':' + #userId + ':' + #page + ':' + #size",
            unless = "#result == null || #result.records.isEmpty()")
    public Page<Follow> getFollowersList(Integer userId, Integer page, Integer size) {
        Page<Follow> followPage = new Page<>(page, size);
        return followMapper.selectPage(followPage, new QueryWrapper<Follow>()
                .eq("following_id", userId)
                .orderByDesc("create_time"));
    }

    /**
     * 获取用户的统计数据
     * @param userId 用户ID
     */
    @Override
    @Cacheable(value = "userStats",
            key = "T(com.ms.constant.CacheConstant).USER_STATS + ':' + #userId",
            unless = "#result == null")
    public UserStats getUserStats(Integer userId) {
        UserStats userStats = userStatsMapper.selectById(userId);
        if (userStats == null) {
            // 如果不存在统计数据，则初始化
            initializeUserStats(userId);
            userStats = userStatsMapper.selectById(userId);
        }
        return userStats;
    }

    /**
     * 初始化用户的统计数据
     * @param userId 用户ID
     */
    @Override
    public void initializeUserStats(Integer userId) {
        UserStats userStats = UserStats.builder()
                .userId(userId)
                .followersCount(0)
                .followingCount(0)
                .build();
        userStatsMapper.insert(userStats);
        log.info("初始化用户 {} 的统计数据", userId);
    }

    /**
     * 更新用户的关注数
     * @param userId 用户ID
     * @param delta  变化量
     */
    private void updateUserFollowingCount(Integer userId, int delta) {
        userStatsMapper.update(null, new UpdateWrapper<UserStats>()
                .eq("user_id", userId)
                .setSql("following_count = following_count + " + delta)
                .setSql("update_time = NOW()"));
    }

    /**
     * 更新用户的粉丝数
     *
     * @param userId 用户ID
     * @param delta  变化量
     */
    private void updateUserFollowersCount(Integer userId, int delta) {
        userStatsMapper.update(null, new UpdateWrapper<UserStats>()
                .eq("user_id", userId)
                .setSql("followers_count = followers_count + " + delta)
                .setSql("update_time = NOW()"));
    }
}